/**
 * Copyright 2002-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package fr.generali.accueilclient.utils.spel.mapping.support;

import java.util.Map;

import org.springframework.core.convert.converter.Converter;

import fr.generali.accueilclient.utils.spel.mapping.Mapper;

/**
 * MapperBuilder that builds {@link SpelMapper} instances.
 * 
 * @author Keith Donald
 */
final class SpelMapperBuilder<S, T> implements MapperBuilder<S, T> {

    /**
     * 
     */
    private final SpelMapper mapper;

    /**
     * 
     */
    private Converter<String, String> sourceFieldDecorator;

    /**
     * 
     */
    private Converter<String, String> targetFieldDecorator;

    /**
     * 
     */
    public SpelMapperBuilder() {
        this.mapper = new SpelMapper();
    }

    /**
     * @param sourceType source type
     * @param targetType target type
     */
    public SpelMapperBuilder(Class<S> sourceType, Class<T> targetType) {
        this.mapper = new SpelMapper();
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> setAutoMappingEnabled(boolean autoMappingEnabled) {
        this.mapper.setAutoMappingEnabled(autoMappingEnabled);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> setSourceFieldDecorator(Converter<String, String> sourceFieldDecorator) {
        this.sourceFieldDecorator = sourceFieldDecorator;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> setTargetFieldDecorator(Converter<String, String> targetFieldDecorator) {
        this.targetFieldDecorator = targetFieldDecorator;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String field) {
        this.mapper.addFieldToFieldMapping(decorate(field, sourceFieldDecorator),
                        decorate(field, targetFieldDecorator), null, null);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String field, Converter< ? , ? > converter) {
        this.mapper.addFieldToFieldMapping(decorate(field, sourceFieldDecorator),
                        decorate(field, targetFieldDecorator), converter, null);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String field, Mapper< ? , T> mapper) {
        this.mapper.addFieldToMultiFieldMapping(decorate(field, sourceFieldDecorator), mapper, null);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String sourceField, String targetField) {
        this.mapper.addFieldToFieldMapping(decorate(sourceField, sourceFieldDecorator),
                        decorate(targetField, targetFieldDecorator), null, null);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String sourceField, String targetField, Converter< ? , ? > converter) {
        this.mapper.addFieldToFieldMapping(decorate(sourceField, sourceFieldDecorator),
                        decorate(targetField, targetFieldDecorator), converter, null);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper) {
        this.mapper.addMultiFieldToFieldMapping(decorateFields(fields, sourceFieldDecorator), mapper, null);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addAssemblerMapping(String field, Converter< ? extends Map< ? , ? >, ? > assembler) {
        this.mapper.addMultiFieldToFieldMapping(decorate(field, sourceFieldDecorator),
                        decorate(field, targetFieldDecorator), assembler, null);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String field, String condition) {
        this.mapper.addFieldToFieldMapping(decorate(field, sourceFieldDecorator),
                        decorate(field, targetFieldDecorator), null, condition);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String field, Converter< ? , ? > converter, String condition) {
        this.mapper.addFieldToFieldMapping(decorate(field, sourceFieldDecorator),
                        decorate(field, targetFieldDecorator), converter, condition);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String field, Mapper< ? , T> mapper, String condition) {
        this.mapper.addFieldToMultiFieldMapping(decorate(field, sourceFieldDecorator), mapper, condition);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField, String condition) {
        this.mapper.addFieldToFieldMapping(decorate(sourceField, sourceFieldDecorator),
                        decorate(targetField, targetFieldDecorator), null, condition);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField,
                    Converter< ? , ? > converter, String condition) {
        this.mapper.addFieldToFieldMapping(decorate(sourceField, sourceFieldDecorator),
                        decorate(targetField, targetFieldDecorator), converter, condition);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String[] fields, Mapper<S, T> mapper, String condition) {
        this.mapper.addMultiFieldToFieldMapping(decorateFields(fields, sourceFieldDecorator), mapper, condition);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addNestedMapper(Mapper< ? , ? > nestedMapper) {
        this.mapper.addNestedMapper(nestedMapper, null);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addNestedMapper(Mapper< ? , ? > nestedMapper, Converter< ? , ? > converter) {
        this.mapper.addNestedMapper(nestedMapper, new ConverterMappingTargetFactory(converter));
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConverter(Converter< ? , ? > converter) {
        this.mapper.getConverterRegistry().addConverter(converter);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> setExcludedFields(String... fields) {
        // TODO
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public Mapper<S, T> getMapper() {
        return (Mapper<S, T> ) this.mapper;
    }

    /**
     * @param field field
     * @param decorator decorator
     * @return field decorated
     */
    private String decorate(String field, Converter<String, String> decorator) {
        return (decorator != null) ? decorator.convert(field) : field;
    }

    /**
     * @param fields fields
     * @param decorator decorator
     * @return decorated fields
     */
    private String[] decorateFields(String fields[], Converter<String, String> decorator) {
        for (int i = 0; i < fields.length; i++) {
            fields[i] = decorate(fields[i], decorator);
        }
        return fields;
    }

}
